﻿using System;
using System.IO;
using Prjinit.Templates.Core;
using Prjinit.Vcses.Core;
using XenoGears.Functional;
using XenoGears.CommandLine;
using XenoGears.CommandLine.Annotations;
using XenoGears.CommandLine.Exceptions;
using XenoGears.Logging.Formatters;
using XenoGears.Strings;

namespace Prjinit
{
    [Config("prjinit")]
    [Shortcut("", Priority = 1)]
    [Shortcut("template", Priority = 2)]
    [Shortcut("name", Priority = 3)]
    [Shortcut("template name", Priority = 4)]
    [Shortcut("name template", Priority = 5)]
    [Shortcut("template name target", Priority = 4)]
    [Shortcut("name template target", Priority = 5)]
    internal class Config : CommandLineConfig
    {
        [Param("name", "prj-name", "project-name", Description = "Name of the project. Defaults to the name of target dir.")]
        public String ProjectName { get; private set; }
        private static bool ValidateProjectName(String name) { return name.IsMatch(@"^\w*$"); }
        private static String DefaultProjectName { get { return null; } }

        public Template Template { get; private set; }
        [Param("template", "template-name", Description = "Codebase template.")]
        private String TemplateName { get; set; }
        private static bool ValidateTemplateName(String name) { return Template.Resolve(name) != null; }
        private static String DefaultTemplateName { get { return Template.Default; } }

        public Vcs Vcs { get; private set; }
        [Param("vcs", "vcs-name", "vcs-provider", Description = "VCS provider.")]
        private String VcsName { get; set; }
        private static bool ValidateVcsName(String name) { return Vcs.Resolve(name) != null; }
        private static String DefaultVcsName { get { return Vcs.Default; } }

        [Param("repo", "vcs-repo", Description = "VCS repository, e.g. https://prjinit.googlecode.com/hg/.")]
        public String VcsRepo { get; private set; }
        private static String DefaultVcsRepo { get { return null; } }

        [Param("target", "dir", "target-dir", Description = "Path to target directory.")]
        public DirectoryInfo TargetDir { get; private set; }
        private static DirectoryInfo DefaultTargetDir { get { return new DirectoryInfo(Environment.CurrentDirectory); } }

        public new static Config Current { get { return (Config)CommandLineConfig.Current; } }
        private Config(String[] args)
            : base(args)
        {
            Debug.EnsureBlankLine();
            Debug.WriteLine("Applying configuration...");

            if (ProjectName.IsNullOrEmpty())
            {
                Debug.WriteLine("Project name is empty. Defaulting to target dir...");
                ProjectName = TargetDir.Name;
                Debug.WriteLine("Project name has been defaulted to {0}.", ProjectName);
            }

            Debug.WriteLine("Resolving template {0}...", Template);
            Template = Template.Resolve(TemplateName);
            if (Template == null) throw new ConfigException("Failed to resolve template \"{0}\".", TemplateName.ToLog());
            else Debug.WriteLine("Resolved template as {0}.", Template);

            Debug.WriteLine("Resolving VCS {0}...", Vcs);
            Vcs = Vcs.Resolve(VcsName);
            if (Vcs == null) throw new ConfigException("Failed to resolve VCS \"{0}\".", VcsName.ToLog());
            else Debug.WriteLine("Resolved VCS as {0}.", Vcs);

            if (TargetDir.Exists)
            {
                var subdirs = TargetDir.GetDirectories();
                var subfiles = TargetDir.GetFiles();
                if (subdirs.IsNotEmpty() || subfiles.IsNotEmpty())
                {
                    Debug.EnsureTwoBlankLines().WriteLine(">>>>>>>>>USER PROMPT<<<<<<<<<");
                    var cur = Console.CursorLeft;
                    Debug.Write("Target directory ain't empty. Proceeding will delete its contents. Okay? ");
                    var debug_writes_to_console = Console.CursorLeft != cur;
                    if (!debug_writes_to_console) Console.Write("Target directory ain't empty. Proceeding will delete its contents. Okay? ");
                    var answer = (Console.ReadLine() ?? String.Empty).ToLower();
                    if (!debug_writes_to_console) Debug.WriteLine(answer);
                    Debug.WriteLine(">>>>>>>>>USER PROMPT<<<<<<<<<").EnsureTwoBlankLines();

                    if (answer == "y" || answer == "yes" || answer == "yeh" || answer == "aye" || answer == "yup")
                    {
                        Debug.WriteLine("Cleaning up target directory at {0}...", TargetDir.ToLog());

                        foreach (var subdir in subdirs)
                        {
                            Debug.WriteLine("Deleting subdirectory at {0}...", subdir.ToLog());
                            subdir.Delete(true);
                            Debug.WriteLine("Subdirectory successfully deleted at {0}.", subdir.ToLog());
                        }

                        foreach (var subfile in subfiles)
                        {
                            Debug.WriteLine("Deleting subfile at {0}...", subfile.ToLog());
                            subfile.Delete();
                            Debug.WriteLine("Subfile successfully deleted at {0}.", subfile.ToLog());
                        }

                        Debug.WriteLine("Target directory successfully cleaned up at {0}.", TargetDir.ToLog());
                    }
                    else
                    {
                        throw new Exception("Aborted by the user.");
                    }
                }
            }

            Debug.WriteLine("Configuration has been successfully applied.");
        }
    }
}
